package net.penguinmenac3.math;
/**
 * Represents an extended matrix. (a|b)
 * 
 * Gives some calculation features.
 * Required for gausian algorithm.
 * 
 * @author Michael
 */
public class ExtendedMatrix extends Matrix {
	private MathObject[][] e;
	
	/**
	 * The first dimension are rows and the second are columns.
	 * e is a second matrix that represents the extended matrix.
	 * @param m
	 */
	public ExtendedMatrix (MathObject[][] m, MathObject[][] e) {
		super(m);
		if (e == null) {
			throw new NullPointerException();
		}
		if (e.length == 0) {
			throw new IllegalArgumentException("Matrix must not have 0 rows");
		}
		if (e.length != m.length) {
			throw new IllegalArgumentException("The extended array and the m array must have the same dimension.");
		}
		int columns = e[0].length;
		for (int i = 0; i < e.length; i++) {
			if (e[i].length != columns) {
				throw new IllegalArgumentException("The amount of columns must be constant per row");
			}
		}
		this.e = e;
	}
	
	/**
	 * Mutiply a row by a given multiplier.
	 * @param index
	 * @param multiplier
	 */
	public void multiplyRow(int index, MathObject multiplier) {
		if (index >= e.length || index < 0) {
			throw new IndexOutOfBoundsException();
		}
		super.multiplyRow(index, multiplier);
		for (int i = 0; i < e[index].length; i++) {
			e[index][i] = e[index][i].multiply(multiplier);
		}
	}
	
	/**
	 * Divide a row by a given multiplier.
	 * @param index
	 * @param multiplier
	 */
	public void divideRow(int index, MathObject multiplier) {
		if (index >= e.length || index < 0) {
			throw new IndexOutOfBoundsException();
		}
		super.divideRow(index, multiplier);
		for (int i = 0; i < e[index].length; i++) {
			e[index][i] = e[index][i].divide(multiplier);
		}
	}
	
	/**
	 * Subtract second from first row.
	 * Hint: Second row remains unchanged.
	 * @param index
	 * @param multiplier
	 */
	public void subtractRows(int index, int index2) {
		if (index >= e.length || index2 >= e.length || index < 0 || index2 < 0) {
			throw new IndexOutOfBoundsException();
		}
		super.subtractRows(index, index2);
		for (int i = 0; i < e[index].length; i++) {
			e[index][i] = e[index][i].subtract(e[index2][i]);
		}
	}
	
	/**
	 * Add second to first row.
	 * Hint: Second row remains unchanged.
	 * @param index
	 * @param multiplier
	 */
	public void addRows(int index, int index2) {
		if (index >= e.length || index2 >= e.length || index < 0 || index2 < 0) {
			throw new IndexOutOfBoundsException();
		}
		super.addRows(index, index2);
		for (int i = 0; i < e[index].length; i++) {
			e[index][i] = e[index][i].add(e[index2][i]);
		}
	}
	
	/**
	 * Return the row at the given index.
	 * @param index
	 * @return
	 */
	public MathObject[] getRow(int index) {
		if (index >= m.length || index < 0) {
			throw new IndexOutOfBoundsException();
		}
		return m[index];
	}
	
	/**
	 * Return the number of rows.
	 * @return
	 */
	public int getRowCount() {
		return m.length;
	}
	
	/**
	 * Return the number of columns.
	 * @return
	 */
	public int getColumnCount() {
		return m[0].length;
	}
	
	/**
	 * Returns the extended matrix.
	 * @return
	 */
	public MathObject[][] getExtended() {
		return e;
	}
	
	/**
	 * Return a formated String.
	 */
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < m.length; i++) {
			for (int j = 0; j < m[i].length-1; j++) {
				sb.append(m[i][j]);
				sb.append(", ");
			}
			sb.append(m[i][m.length-1]);
			sb.append(" | ");
			
			for (int j = 0; j < e[i].length-1; j++) {
				sb.append(e[i][j]);
				sb.append(", ");
			}
			sb.append(e[i][e[i].length-1]);
			sb.append("\n");
		}
		return sb.toString();
	}
	
	/**
	 * Sort a matrix by first non null index.
	 */
	public void sort() {
		for (int i = 0; i < m.length; i++) {
			int max = Integer.MAX_VALUE;
			int maxID = 0;
			for (int j = i; j < m[i].length; j++) {
				int compare = new Vector(getRow(j))
						.getFirstIndexOfNonNullElement();
				if (compare < max) {
					maxID = j;
					max = compare;
				}
			}
			if (max != Integer.MAX_VALUE) {
				swap(i, maxID);
			}
		}
	}
	
	private void swap(int index1, int index2) {
		MathObject[] temp = m[index1];
		m[index1] = m[index2];
		m[index2] = temp;
		temp = e[index1];
		e[index1] = e[index2];
		e[index2] = temp;
	}
}
